/*
 * Copyright (c) 2007 T.Cogghe, D.Leroux, S.Lévignac, S.Rives
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "Image.hh"


Image* Image::Dark::imgNoir=NULL;
double Image::Dark::absorption=.5;
Image::Dark::Algo Image::Dark::algo=QteAbs;
Image::Dark::Mode Image::Dark::mode=Gate;

const unsigned char Image::black[4]={0,0,0,0};





Image::Seuil::Seuil(const Image*pic) {
	int histogram[256];
	int max=0;
	int accum=0;
	//int absorb=0;
	//int goal=(int)(.997*pic->largeur()*pic->hauteur());
	memset(histogram,0,256*sizeof(int));
	for(int y=0;y<pic->hauteur();y++) {
		for(int x=0;x<pic->largeur();x++) {
			++histogram[Image::lum((*pic)(x,y))];
		}
	}

	
	for(int i=0;i<256;i++) {
/*
		if(accum<goal) {		
			accum+=histogram[i];
			if(accum>=goal) {
				k=i;
			}
		}
/*/
		if(max<=histogram[i]) {
			max=histogram[i];
			k=i;
		}
//*/
	}
	// un petit incrément
	k*=4;

	//k=(k+absorb)/2;
	cout<<"Seuil choisi : "<<k<<endl;
}









Image::Dark::Dark(const Image*pic) {

	k=0;
	this->pic=pic;

	if(algo!=ImgNoir) {
		int histogram[256];
		memset(histogram,0,256*sizeof(int));
		for(int y=0;y<pic->hauteur();y++) {
			for(int x=0;x<pic->largeur();x++) {
				++histogram[Image::lum((*pic)(x,y))];
			}
		}
		if(algo==PicFreq) {
			int max=0;
			for(int i=0;i<256;i++) {
				if(max<histogram[i]) {
					max=histogram[i];
					k=i;
				}
			}
		} else if(algo==QteAbs) {
			int npix=(pic->hauteur()*pic->largeur());
			int accum=0;
			for(int i=0;i<256;i++) {
				accum+=histogram[i];
				if(accum>=(int)(npix*absorption)) {
//					cout<<"Seuil dark pour 3/4 pixels : "<<i<<endl;
					k=i;
					i=256;
				}
			}
		}
	} else {
		if(imgNoir==NULL) {
			cerr<<"Attention ! pas d'image noire existante !"<<endl;
		}
	}
//	cout<<"A choisi "<<k<<" comme seuil de Dark (taux d'absorption "<<((Reel)accum/(pic->hauteur()*pic->largeur()))<<"%)"<<endl;
}



const unsigned char*Image::Dark::operator() (const int x,const int y) {
	unsigned char*src=(*pic)(x,y);
	unsigned char*dark;
	if(!src) {
		return black;
	}
	switch(algo) {
	case Aucun:
		return src;
	case QteAbs:
	case PicFreq:
		switch(mode) {
		case Gate:
			return Image::lum(src)<=k ? black : src;
		case Sub:
			tint[0]=(src[0]>k?src[0]-k:0);
			tint[1]=(src[1]>k?src[1]-k:0);
			tint[2]=(src[2]>k?src[2]-k:0);
			return tint;
		};
		break;
	case ImgNoir:
		if(!imgNoir) {
			return src;
		}
		dark=(*imgNoir)(x,y);

		switch(mode) {
		case Gate:
			return Image::lum(src)<=Image::lum(dark) ? black : src;
		case Sub:
			tint[0]=(src[0]>dark[0]?src[0]-dark[0]:0);
			tint[1]=(src[1]>dark[1]?src[1]-dark[1]:0);
			tint[2]=(src[2]>dark[2]?src[2]-dark[2]:0);
			return tint;
		};
		break;
	};
	return black;
}






void Image::_init(Glib::RefPtr<Gdk::Pixbuf> gpb) {
	_pixbuf=gpb;
	px=_pixbuf->get_pixels();
	p=_pixbuf->get_rowstride();
	w=_pixbuf->get_width();
	h=_pixbuf->get_height();
	/* on pourrait aussi hardcoder l'unique RGB 3 canaux de gtk, mais bon.. */
	pxsz=(_pixbuf->get_bits_per_sample()*_pixbuf->get_n_channels())>>3;
	f=NULL;
}




double Image::Seuil_defunct::ctrst(const Image*i,const int x,const int y) {
	double o=Image::lumd((*i)(x,y));
	double a=Image::lumd((*i)(x-1,y))-o;
	double b=Image::lumd((*i)(x+1,y))-o;
	double c=Image::lumd((*i)(x,y-1))-o;
	double d=Image::lumd((*i)(x,y+1))-o;
	a*=a; b*=b; c*=c; d*=d;
	return (a+b+c+d)/o;		/* l'équation aux dimensions est ok, mais après, est-ce que ça veut dire quelque chose, ça, je sais pas trop... */
}

Position& Image::Seuil_defunct::pixACote(const Image*i,const int x,const int y) {
	static Position p;
	double o=Image::lumd((*i)(x,y));
	double a=Image::lumd((*i)(x-1,y))-o;
	double b=Image::lumd((*i)(x+1,y))-o;
	double c=Image::lumd((*i)(x,y-1))-o;
	double d=Image::lumd((*i)(x,y+1))-o;
	a*=a; b*=b; c*=c; d*=d;

	if(a>b&&a>c&&a>d) {
		p.x=x-1;
		p.y=y;
	} else if(b>a&&b>c&&b>d) {
		p.x=x+1;
		p.y=y;
	} else if(c>a&&c>b&&c>d) {
		p.x=x;
		p.y=y-1;
	} else {
		p.x=x;
		p.y=y+1;
	}
	
	return p;
}


Image::Seuil_defunct::Seuil_defunct(const Image*pic) {
	double maxC=0,c;
	int maxx=1,maxy=1;
	Position p={0,0};
	int h=pic->hauteur()-1;
	int w=pic->largeur()-1;
	int x,y;
	for(y=1;y<h;y++) {
		for(x=1;x<w;x++) {
			c=ctrst(pic,x,y);
			if(c>maxC) {
				maxx=x;
				maxy=y;
				maxC=c;
			}
		}
	}
	/* il reste à faire la moyenne entre (maxx,maxy) et le pixel le plus contrastant à son côté */
	p=pixACote(pic,maxx,maxy);
	x=Image::lum((*pic)(maxx,maxy));
	y=Image::lum((*pic)(p.x,p.y));
	if(x>y) {
		k=(x+7*y)/8;
	} else {
		k=(y+7*x)/8;
	}
	cout<<"A choisi le contraste entre les points ("<<p.x<<','<<p.y<<") {"<<Image::lum((*pic)(p.x,p.y))<<"} et ("<<maxx<<','<<maxy<<") {"<<Image::lum((*pic)(maxx,maxy))<<"}, seuil = "<<k<<endl;
}



unsigned char* Image::operator() (int x, int y) const {
	if(x<0||x>=w||y<0||y>=h) {
		return (unsigned char*)black;
	}
	return px+(y*p+x*pxsz);
}

double Image::lumd(unsigned char*pix) {
	/* Gris = 0.2125*Rouge + 0.7154*Vert + 0.0721*Bleu */
	return (0.2125*pix[0] + 0.7154*pix[1] + 0.0721*pix[2]);
}

int Image::lum(unsigned char*pix) {
	//if(!pix) return 0;
	return (int)lumd(pix);
}

unsigned char Image::ajoutSat(unsigned char a,unsigned char b) {
	int x=a+b;
	if(x>255) return 255;
	return (unsigned char)x;
}

int Image::pixInf(int x,int y,int seuil) const {
	return lum((*this)(x,y))<=seuil;
	
}

int Image::pixSup(int x,int y,int seuil) const {
	return lum((*this)(x,y))>seuil;
}

Image& Image::operator += (Image&a) {
	int xmax=w<a.largeur()?w:a.largeur();
	int ymax=h<a.hauteur()?h:a.hauteur();
	unsigned char*pxs,*pxd;

	for(int y=0;y<ymax;y++) {
		for(int x=0;x<xmax;x++) {
			pxd=(*this)(x,y);
			/* si a est une image recadrée, certains pixels seront peut-être nulls */
//			if( (pxs=a(x,y)) ) {
				pxd[0]=ajoutSat(pxs[0],pxd[0]);
				pxd[1]=ajoutSat(pxs[1],pxd[1]);
				pxd[2]=ajoutSat(pxs[2],pxd[2]);
//			}
		}
	}
	return *this;
}


